﻿using Microsoft.EntityFrameworkCore;

namespace AnalyStock.DataManage;

/// <summary>
///     通用数据处理模板
/// </summary>
/// <typeparam name="T"></typeparam>

#region

internal interface IDbTemplate<T>
{
    Task<IList<T>> QueryDataAsync(params string[] args);
    Task<int> SaveDataAsync(IList<T> list, UpdateDataType saveType, string stockcode = default);
    Task<int> DeleDataAsync(IList<T> list, string stockcode = default);
}

internal class DbTemplate<T> : SqiltTableTemplate<T>, IDbTemplate<T> where T : class
{
    public virtual Task<IList<T>> QueryDataAsync(params string[] args)
    {
        return QueryDataTemplateAsync();
    }

    //不能使用 asny void 会导致错误异常点在异步发生，导致无法捕获。    
    public Task<int> SaveDataAsync(IList<T> list, UpdateDataType saveType, string stockCode = default)
    {
        return saveType switch
        {
            UpdateDataType.Insert => InsertDataTemplateAsync(list, stockCode),
            UpdateDataType.Modify => ModifyDataTemplateAsync(list),
            UpdateDataType.ReWrite => ReWriteDataTemplateAsync(list),
            _ => throw new ArgumentNullException()
        };
    }

    public Task<int> DeleDataAsync(IList<T> list, string stockCode = default)
    {
        return DelDataTemplateAsync(list, stockCode);
    }
}

#endregion

/// <summary>
///     泛型数据库结构及方法模板
/// </summary>

#region

internal class SqliteContextTemplate<T> : DbContext where T : class
{
    private readonly string ConnString;

    public SqliteContextTemplate(string stockCode = default)
    {
        var path = string.IsNullOrEmpty(stockCode)
            ? ApplicationParam.BasicDbPath
            : $"{ApplicationParam.DailyDbPath}{stockCode}.db3";
        ConnString = $"Data Source={path}";
        this.ChangeTracker.AutoDetectChangesEnabled=false;
        this.ChangeTracker.QueryTrackingBehavior=QueryTrackingBehavior.NoTracking;
    }

    public DbSet<T> QTable { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        _ = optionsBuilder.UseSqlite(ConnString);
    }
}

internal class SqiltTableTemplate<T> where T : class
{
    internal static async Task<IList<T>> QueryDataTemplateAsync<Torder>
        (string stockcode, Expression<Func<T, bool>> expWhere, Expression<Func<T, Torder>> expOrder)
    {
        await using var dbSqlite = new SqliteContextTemplate<T>(stockcode);
        return await dbSqlite.QTable.Where(expWhere)
                                    .OrderBy(expOrder)
                                    .AsNoTracking()
                                    .ToListAsync()
                                    .ConfigureAwait(false);
    }

    internal static async Task<IList<T>> QueryDataTemplateAsync<Torder>
        (Expression<Func<T, Torder>> expOrder)
    {
        await using var dbSqlite = new SqliteContextTemplate<T>();       
        return await dbSqlite.QTable.OrderBy(expOrder)
                                    .ToListAsync()
                                    .ConfigureAwait(false);
    }

    internal static async Task<IList<T>> QueryDataTemplateAsync()
    {
        await using var dbSqlite = new SqliteContextTemplate<T>();        
        return await dbSqlite.QTable.ToListAsync()
                                    .ConfigureAwait(false);
    }

    //stockCode参数主要是确认数据文件的路径及文件名
    internal static async Task<int> InsertDataTemplateAsync(IList<T> list, string stockCode)
    {
        //判断数据文件是否存在，不存在就并创建数据库文件
        _ = await IsHaveTableAsync(stockCode).ConfigureAwait(false);
        //执行保存数据
        await using var dbSqlite = new SqliteContextTemplate<T>(stockCode);       
        foreach (var item in list)
        {
            if (!await dbSqlite.QTable.ContainsAsync(item).ConfigureAwait(false))
            {
                await dbSqlite.QTable.AddAsync(item).ConfigureAwait(false);
            }
        }
        return await dbSqlite.SaveChangesAsync().ConfigureAwait(false);

        static async Task<bool> IsHaveTableAsync(string stockCode)
        {
            //判断并，创建数据库，包括所有实体Table
            await using var dbSqlite = new SqliteContext(stockCode);
            return await dbSqlite.Database.EnsureCreatedAsync().ConfigureAwait(false);
        }
    }

    internal static async Task<int> ModifyDataTemplateAsync(IList<T> list)
    {
        await using var dbSqlite = new SqliteContextTemplate<T>();       
        foreach (var item in list)
        {
            dbSqlite.QTable.Entry(item).State =
             !await dbSqlite.QTable.ContainsAsync(item).ConfigureAwait(false)
                         ? EntityState.Added : EntityState.Modified;

        }
        return await dbSqlite.SaveChangesAsync().ConfigureAwait(false);
    }

    internal static async Task<int> DelDataTemplateAsync(IList<T> list, string stockCode)
    {
        await using var dbSqlite = new SqliteContextTemplate<T>(stockCode);      
        foreach (var item in list)
        {
            dbSqlite.QTable.Entry(item).State = EntityState.Deleted;
        }

        return await dbSqlite.SaveChangesAsync().ConfigureAwait(false);
    }

    internal static async Task<int> ReWriteDataTemplateAsync(IList<T> list)
    {
        await using var dbSqlite = new SqliteContextTemplate<T>();
        await dbSqlite.QTable.ExecuteDeleteAsync().ConfigureAwait(false);
        await dbSqlite.QTable.AddRangeAsync(list).ConfigureAwait(false);
        return await dbSqlite.SaveChangesAsync().ConfigureAwait(false);
    }
}

#endregion

//数据库实体结构类，临时创建Db文件时需要实体结构
internal class SqliteContext : DbContext
{
    private readonly string ConnString;

    public SqliteContext(string stockCode = default)
    {
        var path = stockCode is null
            ? ApplicationParam.BasicDbPath
            : $"{ApplicationParam.DailyDbPath}{stockCode}.db3";
        ConnString = $"Data Source={path}";
    }

    public DbSet<Daily> Dailies { get; set; }
    public DbSet<DynamicFinancialIndex> DynamicFinancialIndices { get; set; }
    public DbSet<StockBasic> StockBasics { get; set; }
    public DbSet<BakDaily> BakDailies { get; set; }
    public DbSet<SelectStock> SelectStocks { get; set; }
    public DbSet<Conception> Conceptions { get; set; }
    public DbSet<Conception> StockNote { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        _ = optionsBuilder.UseSqlite(ConnString);
    }
}

//泛型实类，多态实现实类查询数据返回
internal class DbQuery_Daily : DbTemplate<Daily>
{
    public override Task<IList<Daily>> QueryDataAsync(params string[] args)
    {
        return QueryDataTemplateAsync(args[0],
            p => p.Trade_date.CompareTo(args[1]) >= 0 && p.Trade_date.CompareTo(args[2]) <= 0,
            p => p.Trade_date);
    }
}

internal class DbQuery_DynamicFinancialIndex : DbTemplate<DynamicFinancialIndex>
{
    public override Task<IList<DynamicFinancialIndex>> QueryDataAsync(params string[] args)
    {
        return QueryDataTemplateAsync(args[0],
            p => p.End_date.CompareTo(args[1]) >= 0 && p.End_date.CompareTo(args[2]) <= 0,
            p => p.End_date);
    }
}

internal class DbQuery_StockInfo : DbTemplate<StockInfo>
{
    public override async Task<IList<StockInfo>> QueryDataAsync(params string[] args)
    {
        var queryBakDaily = await SqiltTableTemplate<BakDaily>.QueryDataTemplateAsync()
            .ConfigureAwait(false);
        var queryBasicStock = await SqiltTableTemplate<StockBasic>.QueryDataTemplateAsync()
            .ConfigureAwait(false);
        var mergeStockInfo = from bkData in queryBakDaily
                             join bsData in queryBasicStock
                             on bkData.Ts_code equals bsData.Ts_code into groupData
                             from mergeData in groupData.DefaultIfEmpty(new StockBasic()) //没有左联上，取默认值，右边主键为空
                             select new StockInfo
                             {
                                 Ts_code = bkData.Ts_code,
                                 Name = bkData.Name,
                                 Pe = bkData.Pe,
                                 Float_mv = bkData.Float_mv,
                                 Total_mv = bkData.Total_mv,
                                 Total_share = bkData.Total_share,
                                 Float_share = bkData.Float_share,
                                 Area = mergeData.Area,
                                 Market = mergeData.Market,
                                 Industry = mergeData.Industry,
                                 Turn_over = bkData.Turn_over,
                                 Vol_ratio = bkData.Vol_ratio,
                                 Trade_date = bkData.Trade_date
                             };
        return mergeStockInfo.ToList();
    }
}

internal class DbQuery_SelectStock : DbTemplate<SelectStock>
{
    public override async Task<IList<SelectStock>> QueryDataAsync(params string[] args)
    {
        return await QueryDataTemplateAsync(n => n.OrderID).ConfigureAwait(false);
    }
}

internal class DbQuery_Conception : DbTemplate<Conception>
{
    public override async Task<IList<Conception>> QueryDataAsync(params string[] args)
    {
        return await QueryDataTemplateAsync(n => n.OrderID).ConfigureAwait(false);
    }
}

internal class DbQuery_StockNote: DbTemplate<StockNote>
{
    public override async Task<IList<StockNote>> QueryDataAsync(params string[] args)
    {
        return await QueryDataTemplateAsync(n => n.Ts_code).ConfigureAwait(false);
    }
}